// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//


#pragma once

#include <pollux/dwio/common/type_with_id.h>
#include <pollux/dwio/parquet/common/level_conversion.h>
#include <pollux/dwio/parquet/thrift/ParquetThriftTypes.h>

namespace kumo::pollux::parquet {
    /// Describes what to extract from leaf repetition / definition
    /// levels for a particular level. Selects between using
    /// DefLevelsToBitmap, DefRepLevelsToList or DefRepLevelsToBitmap
    /// respectively.
    enum class LevelMode { kList, kNulls, kStructOverLists };

    // Describes a Parquet column.
    class ParquetTypeWithId : public dwio::common::TypeWithId {
    public:
        // Occurs in 'column' for non-leaf nodes.
        static constexpr uint32_t kNonLeaf = ~0;

        ParquetTypeWithId(
            TypePtr type,
            std::vector<std::unique_ptr<TypeWithId> > &&children,
            uint32_t id,
            uint32_t maxId,
            uint32_t column,
            std::string name,
            std::optional<thrift::Type::type> parquetType,
            std::optional<thrift::LogicalType> logicalType,
            std::optional<thrift::ConvertedType::type> convertedType,
            uint32_t maxRepeat,
            uint32_t maxDefine,
            bool isOptional,
            bool isRepeated,
            int32_t precision = 0,
            int32_t scale = 0,
            int32_t typeLength = 0)
            : TypeWithId(type, std::move(children), id, maxId, column),
              name_(name),
              parquetType_(parquetType),
              logicalType_(std::move(logicalType)),
              convertedType_(convertedType),
              maxRepeat_(maxRepeat),
              maxDefine_(maxDefine),
              isOptional_(isOptional),
              isRepeated_(isRepeated),
              precision_(precision),
              scale_(scale),
              typeLength_(typeLength) {
        }

        bool isLeaf() const {
            // Negative column ordinal means non-leaf column.
            return static_cast<int32_t>(column()) >= 0;
        }

        const ParquetTypeWithId &parquetChildAt(uint32_t index) const {
            return *reinterpret_cast<const ParquetTypeWithId *>(childAt(index).get());
        }

        const ParquetTypeWithId *parquetParent() const {
            return reinterpret_cast<const ParquetTypeWithId *>(parent());
        }

        /// Fills 'info' and returns the mode for interpreting levels.
        LevelMode makeLevelInfo(LevelInfo &info) const;

        std::vector<std::unique_ptr<ParquetTypeWithId::TypeWithId> > moveChildren()
        const &&;

        const std::string name_;
        const std::optional<thrift::Type::type> parquetType_;
        const std::optional<thrift::LogicalType> logicalType_;
        const std::optional<thrift::ConvertedType::type> convertedType_;
        const uint32_t maxRepeat_;
        const uint32_t maxDefine_;
        const bool isOptional_;
        const bool isRepeated_;
        const int32_t precision_;
        const int32_t scale_;
        const int32_t typeLength_;

        // True if this is or has a non-repeated leaf.
        bool hasNonRepeatedLeaf() const;
    };

    using ParquetTypeWithIdPtr = std::shared_ptr<const ParquetTypeWithId>;
} // namespace kumo::pollux::parquet
